using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using SharpMud.Server.Systems.Scripting.Runtime;
using SharpMud.Server.Systems.Scripting.Processing.Source;

namespace SharpMud.Server.Systems.Scripting.Processing
{
    internal class CompiledScriptHandle : ICompiledScriptHandle
    {
        private CompiledScriptHandle() { }
        public CompiledScriptHandle(IScriptSourceHandle scriptSource, System.Type definingType)
            :this()
        {
            this.Initialize(scriptSource,definingType);
        }

        private void Initialize(IScriptSourceHandle scriptSource,System.Type definingType)
        {
            this._ScriptSource = scriptSource;
            this._DefiningType = definingType;

            
            if (definingType == null)
                return;

            this._ScriptInstance = definingType.Assembly.CreateInstance(definingType.FullName, false);
            System.Diagnostics.Debug.Assert(this._ScriptInstance != null, "this._ScriptInstance!=null");

            MethodInfo strongScriptMethod = null;
            foreach (MethodInfo mi in definingType.GetMethods(BindingFlags.Instance | BindingFlags.Public))
            {
                if (mi.GetCustomAttributes(typeof(StrongScriptMethodAttribute), false).Length > 0)
                {
                    strongScriptMethod = mi;
                    break;
                }
            }
            _StrongScriptMethod = strongScriptMethod;

        }

        private System.Type _DefiningType;
        protected System.Type DefiningType
        {
            get
            {
                return _DefiningType;
            }
            private set
            {
                _DefiningType = value;
            }
        }

        private MethodInfo _StrongScriptMethod;
        protected MethodInfo StrongScriptMethod
        {
            get
            {
                return _StrongScriptMethod;
            }
            private set
            {
                _StrongScriptMethod = value;
            }
        }

        private object _ScriptInstance;
        protected object ScriptInstance
        {
            get
            {
                return _ScriptInstance;
            }
        }

        private IScriptSourceHandle _ScriptSource;


        #region ICompiledScriptHandle Members

        public IScriptSourceHandle GetSource()
        {
            return _ScriptSource;
        }

        public void Execute(SharpMud.Server.Systems.Scripting.Runtime.ScriptExecutionContext context)
        {
            if (this.Invalidated)
                throw new InvalidOperationException();
            
            if(this._ScriptInstance is Scripting.Runtime.IScript)
            {
                (this._ScriptInstance as Scripting.Runtime.IScript).Execute(context);
                return;
            }
            else if (this.StrongScriptMethod != null)
            {
                List<object> parameters = new List<object>();
                foreach (ParameterInfo pi in this.StrongScriptMethod.GetParameters())
                {
                    parameters.Add(context[pi.Name]);
                }
                object retVal = null;
                retVal = this.StrongScriptMethod.Invoke(this._ScriptInstance, parameters.ToArray());
                context.ReturnResult = retVal;
                return;
            }
            else
            {
                throw new NotImplementedException("The script does not implement the IScript calling convention, and the functionality could not be emulated.");
                return;
            }
        }

        public object StrongExecute(params object[] parameters)
        {
            if (this.Invalidated)
                throw new InvalidOperationException();

            if (this.StrongScriptMethod == null)
                throw new NotImplementedException("The script does not implement the StrongScriptMethod calling convention.");

            return this.StrongScriptMethod.Invoke(this.ScriptInstance, parameters);
        }

        #endregion

        private bool _Invalidated = false;
        public bool Invalidated
        {
            get
            {
                if ((_Invalidated || _Disposing || _Disposed || (_ScriptInstance==null) || (_DefiningType==null)))
                    return true;
                else
                    return false;
            }
            set
            {
                if (value == true)
                {
                    _Invalidated = true;
                }
                else if (_Invalidated == false && value == false)
                {
                    return;
                }
                else
                    throw new InvalidOperationException();
            }
        }

        public object[] GetCustomAttributes(bool inherit)
        {
            return this._DefiningType.GetCustomAttributes(inherit);
        }

        public object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            return this._DefiningType.GetCustomAttributes(attributeType, inherit);
        }

        #region IDisposable Members
        private bool _Disposing=false;
        private bool _Disposed=false;
        public void Dispose()
        {
            lock (this)
            {
                if (_Disposing || _Disposed)
                    return;

                _Disposing = true; _Disposed = false;

                
                _Dispose();

                if(this._ScriptInstance != null)
                {
                    if(this._ScriptInstance is IDisposable)
                    {
                        (this._ScriptInstance as IDisposable).Dispose();
                    }
                    this._ScriptInstance = null;
                }
                this._StrongScriptMethod = null;
                if(this._DefiningType!=null)
                {
                    this._DefiningType = null;
                }
                this._ScriptSource = null;


                _Disposed = true; _Disposing = false;
            }
        }
        protected virtual void _Dispose()
        {}

        #endregion

        internal static ICompiledScriptHandle FromTypeAndSources(System.Type definingType, IList<IScriptSourceHandle> possibleScriptSources)
        {
            if (definingType.IsAbstract || definingType.IsInterface)
                return null;
            if (definingType.GetCustomAttributes(typeof(ScriptSourceAttribute), false).Length <= 0)
                return null;

            ScriptSourceAttribute scriptSourceAttribute = (definingType.GetCustomAttributes(typeof(ScriptSourceAttribute), false)[0] as ScriptSourceAttribute);
            foreach (IScriptSourceHandle possibleScriptSourceHandle in possibleScriptSources)
            {
                if (scriptSourceAttribute.CompareTo(possibleScriptSourceHandle) == 0)
                {
                    return new CompiledScriptHandle(possibleScriptSourceHandle, definingType);
                }
            }
            
            return null;
        }
        internal static ICompiledScriptHandle FromSourceAndTypes(IList<System.Type> possibleDefiningTypes, IScriptSourceHandle scriptSourceHandle)
        {
            ICompiledScriptHandle csh=null;
            foreach (System.Type t in possibleDefiningTypes)
            {
                csh = FromTypeAndSources(t, new List<IScriptSourceHandle>(new IScriptSourceHandle[] { scriptSourceHandle }));
                if (csh != null)
                    break;
            }

            return csh;
        }
    }

    //internal class CompiledStrongScriptHandle : CompiledScriptHandle, ICompiledStrongScriptHandle
    //{
    //    #region ICompiledStrongScriptHandle Members

    //    public object StrongExecute(params object[] parameters)
    //    {
    //        if (this.Invalidated)
    //            throw new InvalidOperationException();

    //        throw new Exception("The method or operation is not implemented.");
    //    }

    //    #endregion
    //}
}
